/**

   net_server.c
   ============
	
   generic server
	
**/


/**

   Includes
   --------
	
   - stdlib.h     : EXIT_XX
   - stdio.h      : printf
   - string.h     : memset
   - unistd.h     : close
   - errno        : errno, EINTR
   - sys/types.h  : SOCK_STREAM
   - sys/socket.h : socket, bind, listen, accept
   - netinet/in.h : struct sockaddr_in, INADDR_ALL
   - arpa/inet.h  : htons, htonl
   - pthread.h    : threads

	
**/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <pthread.h>

#include "net_server.h"







/**

   Function: void* net_thread(void* arg)
   -------------------------------------

   Threads main

**/


void* net_thread(void* arg)
{
  int connFD;
  struct net_server* s;

  /* Retrieve arg */
  s = (struct net_server*)arg;

  while(1)
    {
      /* Accept (mutex protected) */
      pthread_mutex_lock(&(s->lock));
      connFD = accept(s->socket,(struct sockaddr*)NULL, NULL);
      pthread_mutex_unlock(&(s->lock));

      if ( connFD < 0 )
	{
	  return NULL;
	}

      /* handler */
      s->handler(connFD);

      /* Done */
      close(connFD);

    }

  return NULL;
  
}


/**

   Function: struct net_server* net_create(unsigned short port, int queue, unsigned int nthreads, req_handler* handler)
   --------------------------------------------------------------------------------------------------------------------

   Create a net_server

**/


struct net_server* net_create(unsigned short port, int queue, unsigned int nthreads, req_handler* handler)
{
  struct net_server* s;

  /* Allocate structure */
  s = (struct net_server*)malloc(sizeof(struct net_server));
  if (s == NULL)
    {
      return NULL;
    }

  /* Fill it */
  s->port = port;
  s->queue = queue;
  s->nthreads = nthreads;
  s->handler = handler;

  /* Allocate threads */
  s->threads = (pthread_t*)malloc(s->nthreads*sizeof(pthread_t));
  if (s->threads == NULL)
    {
      free(s);
      return NULL;
    }
  
  /* Initialize lock */
  pthread_mutex_init(&(s->lock),NULL);

  /* return net_server */
  return s;
}



/**

   Function: int net_delete(struct net_server* s)
   ----------------------------------------------

   Release net_server

**/


int net_delete(struct net_server* s)
{
  /* Close socket */
  close(s->socket);

  /* Release threads */
  free(s->threads);

  /* Release object */
  free(s);

  return EXIT_FAILURE;
}



/**

   Function: int generic_listen(struct net_server* s)
   --------------------------------------------------

   Generic tcp listening socket (on all interface) creation

**/


int net_listen(struct net_server* s)
{
  int socketFD;
  struct sockaddr_in srv_addr;
  
  /* Socket creation : domain IPV4 (AF_INET), type TCP (SOCK_STREAM)  */
  socketFD = socket(AF_INET,SOCK_STREAM,0);
  if (socketFD < 0)
    {
      return EXIT_FAILURE;
    }
	
  /* Build struct sockaddr_in to listen to */
	
  /* First cleant it */
  memset(&srv_addr, 0, sizeof(struct sockaddr_in));
  /* Address family (IPv4 = AF_INET) */
  srv_addr.sin_family = AF_INET;
  /* Port in network order */
  srv_addr.sin_port = htons(s->port);
  /* Listening address in network order */
  srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
  /* Bind socket to an address */
  if ( bind(socketFD, (struct sockaddr*)&srv_addr, sizeof(struct sockaddr_in)) < 0 )
    {
      return EXIT_FAILURE;
    }
	
  /* Listen for incoming connections */
  if ( listen(socketFD, s->queue) < 0 )
    {
      return EXIT_FAILURE;
    }
  
  /* Set socket */
  s->socket = socketFD;

  return EXIT_SUCCESS;
}


/**

   Function: int net_run(struct net_server* s)
   -------------------------------------------

   Handle requests

**/


int net_run(struct net_server* s)
{
  unsigned short i;

  /* Create threads */
  for(i=0;i<s->nthreads;i++)
    {
      pthread_create(&(s->threads[i]),NULL,net_thread,(void*)s);
    }

  return EXIT_SUCCESS;
}
